package de.htwg_konstanz.infen.aufg1.main;

import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;

import javax.xml.XMLConstants;
import javax.xml.bind.JAXBContext;
import javax.xml.bind.JAXBException;
import javax.xml.bind.Marshaller;
import javax.xml.bind.Unmarshaller;
import javax.xml.validation.Schema;
import javax.xml.validation.SchemaFactory;

import org.xml.sax.SAXException;

import de.htwg_konstanz.infen.aufg1.inputXML.jaxbGenerated.Endpoints;
import de.htwg_konstanz.infen.aufg1.inputXML.jaxbGenerated.EntityTyp;
import de.htwg_konstanz.infen.aufg1.inputXML.jaxbGenerated.ErModel;
import de.htwg_konstanz.infen.aufg1.inputXML.jaxbGenerated.RelationshipType;

/**
 * Date: 29.11.2010 Course: Information Engineering Team: Valentin Durst, Tobias
 * Kutter and Stefan Bernhard
 * 
 * Description: Main Class for transforming the input XML file into a class tree
 * converting this class tree to match the output XML file structe. This class
 * also checks the XML files against the XSD files.
 **/

public class MainClass {

	public static boolean allFilesExist(String... files) {
		for (String string : files) {
			if (!(new File(string)).exists())
				return false;
		}
		return true;
	}

	public static boolean allFilesExist(File... files) {
		for (File file : files) {
			if (!file.exists())
				return false;
		}
		return true;
	}

	public static boolean isHelp(String[] args) throws IOException {
		if (args[0].equals("help") || args[0].equals("--help")
				|| args[0].equals("-help") || args[0].equals("-h")
				|| args[0].equals("/h")) {

			HelpText.getHelp("de");
			return true;
		}
		return false;
	}

	public static void main(String[] args) {

		CustomLogger log = new SimpleCustomLogger();

		if (args.length != 0 && !args[0].equals("")) {
			try {
				if (isHelp(args))
					System.exit(0);
			} catch (IOException e1) {
				e1.printStackTrace();
				System.exit(1);
			}
		} else {
			System.out.println("please define some parameter values.");
			System.exit(0);
		}

		log.print("---- ER-Model Converter ----");
		log.newLine();

		String INPUT_XML, INPUT_XSD, OUTPUT_XML, OUTPUT_XSD;

		INPUT_XML = "xml" + File.separator + "xml_input" + File.separator
				+ "testCases.xml";
		OUTPUT_XML = "xml" + File.separator + "xml_output" + File.separator
				+ "ermodel_output.xml";
		INPUT_XSD = "xml" + File.separator + "xml_input" + File.separator
				+ "input_schema.xsd";
		OUTPUT_XSD = "xml" + File.separator + "xml_output" + File.separator
				+ "relational_schema.xsd";

		if (args.length == 2) {
			INPUT_XML = args[0];
			OUTPUT_XML = args[1];
			INPUT_XSD = "schema_in.xsd";
			OUTPUT_XSD = "schema_out.xsd";
		} else if (args.length == 4) {
			INPUT_XML = args[0];
			INPUT_XSD = args[1];
			OUTPUT_XML = args[2];
			OUTPUT_XSD = args[3];
		}

		if (!allFilesExist(INPUT_XML, INPUT_XSD, OUTPUT_XSD)) {
			log.logErr("one or more files does not exist");
			System.exit(1);
		}

		log.print("Input XML Location: "
				+ (new File(INPUT_XML).getAbsolutePath()));
		log.print("Output XML Location: "
				+ (new File(OUTPUT_XML).getAbsolutePath()));
		log.newLine();
		log.print("Input XML-Schema Location: "
				+ (new File(INPUT_XSD).getAbsolutePath()));
		log.print("Output XML-Schema Location: "
				+ (new File(OUTPUT_XSD).getAbsolutePath()));
		log.newLine();

		try {

			// Create Schema objects
			SchemaFactory sf = SchemaFactory
					.newInstance(XMLConstants.W3C_XML_SCHEMA_NS_URI);
			Schema inputSchema = sf.newSchema(new File(INPUT_XSD));
			Schema outputSchema = sf.newSchema(new File(OUTPUT_XSD));
			log.log("schemas defined");

			// FROM XML TO CLASSES
			JAXBContext jci = JAXBContext
					.newInstance("de.htwg_konstanz.infen.aufg1.inputXML.jaxbGenerated");
			Unmarshaller unmarshaller = jci.createUnmarshaller();
			unmarshaller.setSchema(inputSchema);
			unmarshaller
					.setEventHandler(new javax.xml.bind.helpers.DefaultValidationEventHandler());
			ErModel collection = (ErModel) unmarshaller.unmarshal(new File(
					INPUT_XML));
			log.log("input xml unmarshalled and validated");

			InputInfos ii = new InputInfos(collection);
			OutputHelper oh = new OutputHelper(ii);

			// TRANSFORM THE CLASS TREE TO FIT THE OUTPUT XML STRUCTURE

			// Transform the entities in Tables
			for (EntityTyp et : ii.getAllEntities()) {
				oh.addTable(et.getName(), et.getAttributes().getAttribute(), et
						.getPrimaryKey().getAttribute(), ii
						.getAlternativeKeys(et.getName()));
			}
			log.log("entities transformed in tables");

			// Create the Relationships
			for (RelationshipType rt : ii.getAllRelationships()) {
				// Binary Relations
				Endpoints eps = rt.getEndpoints();
				if (eps.getEndpoint().size() == 2) {
					Relations rels = oh.getRelType(eps);
					// Decides which relation type we have to handle
					switch (rels) {
					case OnetoOne:
						oh.addOneToOneRel(eps, rt);
						break;
					case OnetoN:
						oh.addOneToNRel(eps, rt);
						break;
					case NtoOne:
						oh.addNToOneRel(eps, rt);
						break;
					case NtoN:
						oh.addNToNRel(eps, rt);
						break;
					default:
						break;
					}
				}

				// more then binary Relations
				else {
					oh.addRelationship(eps, rt);
				}
			}
			log.log("all relations transformed");

			// Create the fds from the Primary and the Alternativ Keys, trivial
			// isnt it?
			oh.addFD_auto();

			log.log("functional dependencies set");

			// FROM CLASSES TO XML
			JAXBContext jco = JAXBContext
					.newInstance("de.htwg_konstanz.infen.aufg1.outputXML.jaxbGenerated");
			Marshaller marshaller = jco.createMarshaller();
			marshaller.setSchema(outputSchema);
			marshaller
					.setEventHandler(new javax.xml.bind.helpers.DefaultValidationEventHandler());
			marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true);
			marshaller.setProperty("jaxb.noNamespaceSchemaLocation", (new File(
					OUTPUT_XSD).getAbsolutePath()));
			marshaller.marshal(oh.theConfiguration, new FileOutputStream(
					OUTPUT_XML));
			log.log("output XML marshalled and validated");
			log.newLine();
			log.print("XML file written in "
					+ (new File(OUTPUT_XML).getAbsolutePath()));

		} catch (JAXBException e) {
			log.logErr(e.getStackTrace().toString());
			e.printStackTrace();
		} catch (IOException e) {
			log.logErr(e.getStackTrace().toString());
			e.printStackTrace();
		} catch (SAXException e) {
			log.logErr(e.getStackTrace().toString());
			e.printStackTrace();
		}
	}

}